import os
import sys
from datetime import date,datetime,time
from mmap import mmap,ACCESS_READ
from xlrd import open_workbook,xldate_as_tuple
import numpy

from RunHistory import GetSourcesStations
from CalTqFixOffset_Gauss import CorrectForOffset_G
#from Velocity import Velocity


def OpenCalTorqueXLS(filename):
   book = open_workbook(filename, logfile=open(os.devnull, 'w'))
   # the data is on sheet 0
   # sheet = book.sheet_by_index(0)
   # print sheet.name
   return book
   

def GetSheetData(book):
   #book = self.book
   sheet = book.sheet_by_index(0)
   data = {}
   # Expect row0 to be xlrd.XL_CELL_TEXT type
   col0 = sheet.row_values(0)
   for i,v in enumerate(col0):
      data[v] = sheet.col_values(i,1)

   # Convert excel time to python datetime
   data["datetime"] = []
   data['microdt'] = [] ### dt in microseconds
   
   # convert first timestamp to microseconds
   utc0 = datetime.utcfromtimestamp((data["Time"][0] - 25569) * 86400.0)
   t0list = str(utc0).split(' ')[1].split(':')
   t0 = int(t0list[0])*3600000000 + int(t0list[1])*60000000 + float(t0list[2])*1000000
   
   for i in range(len(data["Time"])):
      xlsdate = xldate_as_tuple(data["Date"][i],book.datemode)
      xlstime = xldate_as_tuple(data["Time"][i],book.datemode)
      floattime = data["Time"][i]
      #print floattime
      data["datetime"].append(datetime(xlsdate[0],xlsdate[1],xlsdate[2],
      xlstime[3],xlstime[4],xlstime[5]))
      
      # convert timestamp to microseconds, get dt in microseconds
      utci = datetime.utcfromtimestamp((floattime - 25569) * 86400.0)
      tlist = str(utci).split(' ')[1].split(':')
      t = int(tlist[0])*3600000000 + int(tlist[1])*60000000 + float(tlist[2])*1000000
      data['microdt'].append(t - t0)

   # Translate values to useful units and measurements
   data['nentries']= len(data['datetime'])
   data['date'] = data['datetime'][0].date()
   data['dt'] = []
   data['x'] = []
   data['position'] = []
   data['torque_inoz'] = []
   data['rpm'] = []
   data['velocity'] = []
   data['max_torque_inoz'] = 0
   data['min_torque_inoz'] = 0

   for i,t in enumerate(data['datetime']):
      data['dt'].append( (t - data['datetime'][0]).seconds)
      #data['x'].append(data['dt'][i].seconds)
      data['position'].append(data['Angle'][i]/360.*3.14159*4.0*2.54)
      data['torque_inoz'].append(data['Tracking Value'][i]*141.612)
      data['velocity'].append(data['RPM'][i] * ((2 * 3.14) / 60) * (2 * 2.54)) #cm/s
      
      if data['Tracking Value'][i]*141.612 > data['max_torque_inoz']:
         data['max_torque_inoz'] = data['Tracking Value'][i]*141.612
      if data['Tracking Value'][i]*141.612 < data['min_torque_inoz']:
         data['min_torque_inoz'] = data['Tracking Value'][i]*141.612
      
      #if i==0:
      #  data['rpm'].append(0)
      #else:
      #   rev = (data['Angle'][i]-data['Angle'][i-1])/360.
      #   denom = ((data['microdt'][1] - data['microdt'][1-i])/60.)
      #   if denom == 0: #would have to integrate... maybe if bin?
      #      rpm = rev / 0.0000001
      #      print 'here'
      #   else:
      #      rpm = rev / denom
      #   data['rpm'].append(rpm)
               
   data['initial_pos'] = data['position'][0]
   data['final_pos'] = data['position'][-1]
   
   fitthis = numpy.zeros([data['nentries'],2])
   fitthis[:,0] = data['dt']
   
   # Smooth the Angle data
   #print data['Angle'][data['nentries']/4:data['nentries']/4+100]
   #fitthis[:,1] = data['Angle']
   #smoothline = SmoothData(fitthis)
   #print smoothline[data['nentries']/4:data['nentries']/4+100,1]
   #data['Angle_smooth'] = list(smoothline[:,1])
   
   #for i,v in enumerate(data['Angle_smooth']):
   #   if i ==0:
   #      data['RPM'].append(0)
   #   else:
   #      rev = (data['Angle_smooth'][i] - data['Angle_smooth'][i-1])
   #      data['RPM'].append( rev / (data['dt'][i] - data['dt'][i-1]))
   
   # Correct for initial offset # this can go? Although, end up doing 
   # same in Gauss
   #for i,d in enumerate(data['position']):
      #data['position'][i] = data['position'][i] #- data['initial_pos']

   return data

### these functions are not used. When scripts are done and cleaned, should these be removed?
def LinearFit(linedata):
   x = linedata[:,0]
   y = linedata[:,1]
   fit = numpy.ma.polyfit(x,y,1)
   #print fit
   return fit

def SmoothData(linedata, type='linear'):
   # Takes a 1D numpy line [p,2] and smooths the data
   smoothline = numpy.zeros(linedata.shape)
   
   if type == 'linear':
      # Use nearest s points to set the central value
      s = 7 # s must be odd, notice the interger division!
      fitline = numpy.zeros([s,2])
      
      #The first s/2 points are just a linear fit to the data
      fitline = linedata[0:s,:]
      fit = LinearFit(fitline)
      for i in range(s/2):
         smoothline[i,:] = [linedata[i,0], fit[0] + linedata[i,0]*fit[1]]
      
      # Now fit the rest of the line based on the neighboring s/2 points
      for i in range(len(linedata[:,0])-s):
         fitline = linedata[i:i+s,:]
         fit = LinearFit(fitline)
         smoothline[i+s/2,:] = [linedata[i+s/2,0], fit[0] + linedata[i+s/2,0]*fit[1]]

      # And finally do the last s/2 points
      fitline = linedata[-s:,:]
      fit = LinearFit(fitline)
      for i in range(s/2):
         smoothline[-i,:] = [linedata[-i,0], fit[0] + linedata[-i,0]*fit[1]]
         
   return smoothline


def AddStraight(tube,dx,nx=100):
   x0 = tube['x'][-1]
   y0 = tube['y'][-1]
   for i in range(nx):
      tube['x'].append(x0 + dx*float(i)/float(nx-1))
      tube['y'].append(y0)
   return tube

def AddBendTube(tube,r,theta,nx=100):
   x0 = tube['x'][-1]
   y0 = tube['y'][-1]
   for i in range(nx):
      tube['x'].append(x0 + 2.0*3.14159*r*theta/360.*float(i)/
      float(nx-1))
      tube['y'].append(y0 + theta*float(i)/float(nx-1))
   return tube

def SubLoops(tube,loops):
   total_length = 750 #cm
   bend = 360.0*loops*tube['x'][-1]/750.0
   length = len(tube['x'])
   for i,x in enumerate(tube['x']):
      tube['y'][i] = tube['y'][i] - bend*x/tube['x'][-1]
   return tube

def AddCalTube():
   tube = {}
   tube['x'] = [0]
   tube['y'] = [432]		###_________ 1.2 turns * 360 = 432, or 3 turns * 360 = 1080 ______###
   tube = AddStraight(tube,47.8)
   tube = AddBendTube(tube,29.106,90)
   tube = AddStraight(tube,136.4-93.5)
   tube = AddBendTube(tube,5.2388,90)
   tube = AddStraight(tube,196.5-144.6)
   tube = AddBendTube(tube,5.238,235)
   tube = AddStraight(tube,228.3-218.0)
   tube = AddBendTube(tube,5.2387,145)
   tube = AddStraight(tube,274.6-241.5)
   tube = AddBendTube(tube,7.618,75)
   tube = AddStraight(tube,309.0-284.6)
   tube = AddBendTube(tube,7.618,75)
   tube = AddStraight(tube,358.8-318.9)
   tube = AddBendTube(tube,7.619,90)
   tube = AddStraight(tube,414.5-370.8)
   tube = AddBendTube(tube,7.619,90)
   tube = AddStraight(tube,445.7-426.5)
   tube = AddBendTube(tube,5.08,108)
   tube = AddStraight(tube,467.5-455.3)
   tube = AddBendTube(tube,7.619377,90)
   tube = AddStraight(tube,523.8-479.5)
   tube = AddBendTube(tube,7.6193,90)
   tube = AddStraight(tube,571.6-535.8)
   tube = AddBendTube(tube,7.619,90)
   tube = AddStraight(tube,627.9-583.6)
   tube = SubLoops(tube, 1.2)
   return tube


def CalTorque(filename):
    book = OpenCalTorqueXLS(filename)
    data = GetSheetData(book)
    data['tube'] = AddCalTube()
    data['XL_filename'] = filename # because there's info in there sometimes
    data['run'] = ''.join(str(data['date']).split('-'))
    
    if data['position'][0] < data['position'][-1]:
        data['direction'] = 'in'
    else:
        data['direction'] = 'out'
            
    # get sources and stations
    if data['direction'] == 'out' and data['final_pos'] > -50 and data['final_pos'] < 50:
        finalPos = data['initial_pos'] ## this due to different naming conventions for retractions
    else:
        finalPos = data['final_pos']
        
    data['source'], data['station'] = GetSourcesStations(str(data['date']), finalPos, filename)
    
    
    # fix offset
    if data['direction'] == 'in' and data['initial_pos'] < 100 and data['source'] != 'weakRa-226':
        data['pos_offsetfix'], data['offset'], data['new_parms'] = CorrectForOffset_G(data['position'], data['torque_inoz'], data['station'])
    else:
        data['pos_offsetfix'], data['offset'] = data['position'], 0
    
    # get velocity from distance versus time spline
    #data['velAtS5'] = VelocityAtS5(data['pos_offsetfix'], data['dt'])
    
    return data



def main(argv = sys.argv):
   if len(argv)>1:
      book = OpenCalTorqueXLS(sys.argv[1])
   else:
      print "Do something like: "
      print "  book = OpenCalTorqueXLS(<filename>)"

if __name__ == "__main__":
   main(sys.argv)